package ue.networking;

import java.io.DataInputStream;
import java.io.IOException;
import java.math.BigInteger;
import java.util.Arrays;

import javax.crypto.spec.SecretKeySpec;

import org.apache.commons.codec.binary.Base64;

import ue.core.Galaxy;

public class ClientInputListener implements Runnable {
	private DataInputStream in;
	
	private Client client;
	
	public ClientInputListener(DataInputStream in, Client client) {
		this.in = in;
		this.client = client;
	}
	
	@Override
	public void run() {
		for(;;) {
			if(client.getStatus() != Client.Status.UNIDENTIFIED && 
					client.getStatus() != Client.Status.DISCONNECTED) {
				byte command = receiveCommand();
				
				switch(command) {
				case 0:
					System.out.println(Galaxy.getTimestamp() + "Taxes paid, you may continue living.");
					client.lastPing = System.currentTimeMillis();
					break;
				case 1:
					int x = Integer.MIN_VALUE;
					int y = Integer.MIN_VALUE;
					try {
						x = in.readInt();
						y = in.readInt();
					} catch (IOException e) {
						e.printStackTrace();
					}
					
					if(x != Integer.MIN_VALUE && y != Integer.MIN_VALUE) {
						client.sendLocation(Galaxy.getLocation(x, y));
					}
					break;
				}
				
//				sendLocation(Galaxy.locations.get(0));
//				System.exit(0);
			} else if(client.getStatus() == Client.Status.DISCONNECTED) {
				return;
			} else {
				System.out.println(Galaxy.getTimestamp() + "Requesting Client " + client.getID() + " Identification");
				identify();
			}
			
			try {
				Thread.sleep(100);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
	}
	
	private void identify() {
		client.sendCommand((byte)1);
		
		BigInteger p = new BigInteger(Arrays.copyOf(Cryptography.generateDHPublicP(512, 128).toByteArray(), 64));
		BigInteger g = Cryptography.generateDHPublicG(p);
		
		client.sendBigInteger(p);
		client.sendBigInteger(g);
		
		BigInteger a = Cryptography.generateDHPrivateNumber(p);
		
		BigInteger A = Cryptography.calculateDHTransferNumber(a, p, g);
		
		client.sendBigInteger(A);
		
		BigInteger B = receiveBigInteger();
		
//		System.out.println("A: " + A);
//		System.out.println("B: " + B);
//		System.out.println("a: " + a);
//		System.out.println("p: " + p);
//		System.out.println("g: " + g);
		BigInteger key = Cryptography.calculateDHKey(B, a, p);
		System.out.println(key);
		System.out.println(key.bitLength());
		
		SecretKeySpec keySpec = Cryptography.generateAESKey(key);
		
		byte[] nameEncrypted = receiveBytes(128);
//		System.out.println(new String(nameEncrypted));
		
		byte[] passwordEncrypted = receiveBytes(128);
//		System.out.println(new String(passwordEncrypted));
		
		String name = new String(Cryptography.decodeAES(keySpec, nameEncrypted));
		String password = Base64.encodeBase64String(Cryptography.decodeAES(keySpec, passwordEncrypted));
		
		System.out.println(name + ":" + password);
		
		if(Galaxy.server.accountSystem.verifyAccount(name, password) != -1) {
			client.setName(name);
			client.setPassword(password);
			client.setStatus(Client.Status.IDENTIFIED);
			
			System.out.println("Client " + client.getID() + " Identification successful");
			
			client.sendCommand((byte)2);
		} else {
			System.out.println("Client " + client.getID() + " Identification failed");

			client.sendCommand((byte)3);
			client.disconnect();
		}
	}
	
	protected byte receiveCommand() {
		byte[] bytes = new byte[1];
		try {
			in.read(bytes);
		} catch (IOException e) {
			e.printStackTrace();
			client.disconnect();
		}
		return bytes[0];
	}
	
	private byte[] receiveBytes(int length) {
		byte[] bytes = new byte[length];
		try {
			in.read(bytes);
		} catch (IOException e) {
			e.printStackTrace();
		}
		return removeZeros(bytes);
	}
	
	private BigInteger receiveBigInteger() {
		byte[] bytes = new byte[64];
		try {
			in.read(bytes);
		} catch (IOException e) {
			e.printStackTrace();
		}
		return new BigInteger(removeZeros(bytes));
	}
	
	private byte[] removeZeros(byte[] bytes) {
		while(bytes[bytes.length - 1] == 0) {
			bytes = Arrays.copyOf(bytes, bytes.length - 1);
		}
		
		return bytes;
	}

	public void close() throws IOException {
		in.close();
	}
}